/*
  类的继承
    如果两个类A和B，如果可以描述为：B 是 A，则 A和B形成继承关系

    如果B是A，则：
      B继承自A
      A派生B
      B是A的子类
      A是B的父类

    新的关键词：
      extends：继承，用于类的定义
      super
        直接当作函数调用，表示父类构造函数
        如果当作对象调用，表示父类的原型

    注意:
      ES6要求，如果定义了constructor，并且该类是子类，则必须在constructor的第一行手动调用父类的构造函数
        如果不写constructor，则会由默认的构造器，该构造器需要的参数和父类一致，并且自动调用父类构造器

      抽象类
        一般是父类，不能通过该类创建对象
    
      正常情况下，this的指向，this始终指向具体类的对象
*/

// es5
/* function Animal(type, name, age, sex) {
  this.type = type;
  this.name = name;
  this.age = age;
  this.sex = sex;
}

Animal.prototype.print = function () {
  console.log(`${this.type} ${this.name} ${this.age} ${this.sex}`);
};

function Dog(name, age, sex) {
  Animal.call(this, "犬类", name, age, sex);
}
const d = new Dog("旺财", 3, "公");
Object.setPrototypeOf(Dog.prototype, Animal.prototype);
d.print();
console.log(d); */

class Animal {
  constructor(type, name, age, sex) {
    if (new.target === Animal) {
      throw new TypeError("你不能通过new创建Animal，只能通过子类创建对象");
    }
    this.type = type;
    this.name = name;
    this.age = age;
    this.sex = sex;
  }
  print() {
    console.log(`${this.type} ${this.name} ${this.age} ${this.sex}`);
  }
  jiao() {
    throw new Error("动物怎么叫？");
  }
}

class Dog extends Animal {
  constructor(name, age, sex) {
    super("犬类", name, age, sex);
    this.loves = "吃骨头";
  }

  print() {
    super.print();
    console.log(`${this.loves}`);
  }

  jiao() {
    console.log("狗叫：汪汪汪");
  }
}

const a = new Animal("犬类", "旺财", 3, "公");
const d = new Dog("旺财", 3, "公");
console.log(d);
